home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / strategy / xsok-1.000 / xsok-1 / xsok-1.01 / src / move.c < prev    next >
C/C++ Source or Header  |  1994-11-24  |  12KB  |  426 lines

  1. /*****************************************************************************/
  2. /*                                         */
  3. /*                                         */
  4. /*    Xsok version 1.00 -- module move.c                     */
  5. /*                                         */
  6. /*    Detection of valid moves, and effect handling functions.         */
  7. /*    Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de)             */
  8. /*    November-1994                                 */
  9. /*    see COPYRIGHT.xsok for Copyright details                 */
  10. /*                                         */
  11. /*                                         */
  12. /*****************************************************************************/
  13. #ifndef _POSIX_SOURCE
  14. #define _POSIX_SOURCE
  15. #endif
  16. #include "xsok.h"
  17.  
  18. #define SAVE_STATE    /* variant which should flicker less */
  19. #define SELECTOR_HACK    /* ugly hack to allow Cyberbox selectors */
  20.  
  21. static int dxtab[4] = { 0, -1, 0, 1 };
  22. static int dytab[4] = { -1, 0, 1, 0 };
  23.  
  24. char *movetab = NULL;
  25. int numalloc = 0;
  26.  
  27. /* declare the unmovable blocker which prevents race conditions */
  28. static struct objects blocked = { 1, 0, 0, 0, 0, 0, 0, 0 };
  29.  
  30. static void storemove(int res, int dir) {   
  31.     if (res > 1)
  32.     ++game.n_pushes;
  33.     if (game.n_moves == numalloc)
  34.     movetab = realloc_(movetab, numalloc += 256);
  35.     if (game.stored_moves > game.n_moves && movetab[game.n_moves] == dir) {
  36.     /* This is a redo, but the user retyped it. Be nice. */
  37.     ++game.n_moves;
  38.     } else {
  39.     /* this is a new move: reset number of stored moves (no redo beyond this one) */
  40.     if (game.bookmark > game.n_moves)
  41.         game.bookmark = game.n_moves;
  42.     movetab[game.n_moves++] = dir;
  43.     game.stored_moves = game.n_moves;    /* reset the redo limit */
  44.     }
  45. }
  46.  
  47.  
  48. #define DOPAINT(x1,y1,x2,y2)    if (gamegraphic) doPaint(x1,y1,x2,y2);
  49.  
  50. #ifdef SAVE_STATE
  51. static struct walls *savemap[MAXROW][MAXCOL];
  52. static struct objects *saveobj[MAXROW][MAXCOL];
  53. static struct objects saveinstance[MAXINSTANCES];
  54.  
  55. static void save_state(void) {
  56.     int x, y;
  57.     gamegraphic = 0;
  58.     memcpy(savemap, map, sizeof(map));    /* dereferenced objects are readonly */
  59.     memcpy(saveinstance, instance, sizeof(instance));
  60.     memcpy(saveobj, obj, sizeof(obj));
  61.     for (y = 2; y < game.numrows-2; ++y)
  62.     for (x = 2; x < game.numcols-2; ++x)
  63.         if (obj[y][x]) {
  64.         int i;
  65.         i = obj[y][x] - instance;
  66.         saveobj[y][x] = saveinstance + i;
  67.         }
  68. }
  69.  
  70. static void update_state(void) {
  71.     int x, y;
  72.     gamegraphic = 1;
  73.     for (y = 2; y < game.numrows-2; ++y)
  74.     for (x = 2; x < game.numcols-2; ++x) {
  75.         int pica, picb;
  76.         pica = obj[y][x] ? obj[y][x]->pic : -1;
  77.         picb = saveobj[y][x] ? saveobj[y][x]->pic : -1;
  78.         if (pica != picb || map[y][x] != savemap[y][x])
  79.         doPaint(x,y, x,y);
  80. #if 0
  81.         if (pica != picb) {
  82.         printf("At (%2d,%2d): pics %d and %d differ\n", x, y, pica, picb);
  83.         }
  84. #endif
  85.     }
  86. }
  87. #define SAVE    save_state();
  88. #define UPDATE    update_state();
  89. #else
  90. #define SAVE    gamegraphic = 0;
  91. #define UPDATE  { gamegraphic = 1; doPaint(2,2, game.numcols-3,game.numrows-3); }
  92. #endif
  93.  
  94. void graphics_control(int on) {
  95.     switch (on) {
  96.     case Disable:
  97.     SAVE;
  98.     break;
  99.     case Enable:
  100.     gamegraphic = 1;
  101.     break;
  102.     case EnableAndRedraw:
  103.     UPDATE;
  104.     break;
  105.     }
  106. }
  107.  
  108.  
  109. /* Check if object on square (x,y) has enough power to move in direction dir */
  110. /* If it has, do the move and return 1 or 2 (1 if object moved alone)        */
  111.  
  112. static int do_move(int x, int y, int dir) {
  113.     struct objects *ip;
  114.     struct walls *wp;
  115.     int dx, dy, tx, ty;
  116.     int dirbits, power;
  117. #ifdef SELECTOR_HACK
  118.     static struct objects *sel_ip = NULL;
  119.     struct objects *new_sel_ip;
  120.     int forbidden = 0;
  121.     int sel_enter = 0, sel_leave = 0;
  122.  
  123.     if (obj[y][x]->pic == 7)
  124.     sel_leave = 1;
  125. #endif
  126.  
  127.     dx = dxtab[dir];
  128.     dy = dytab[dir];
  129.     dirbits = 1 << dir;
  130.     
  131.     tx = x;
  132.     ty = y;
  133.  
  134.     
  135.     wp = map[ty][tx];
  136.     ip = obj[ty][tx];    /* what moves? */
  137.     power = 0;
  138.     do {
  139.     if (ip->chr)            /* involves object already moved? */
  140.         return 0;
  141.     if (!(ip->movedir & dirbits))    /* object isn't allowed to go this way */
  142.         return 0;
  143.     if (ip->pushdir & dirbits)    /* does this one help pushing? */
  144.         power += ip->power;
  145.     power -= ip->weight;
  146.     if (power < 0)            /* does our power suffice? */
  147.         return 0;            /* it's not enough to check this at the end of the loop */
  148.     /* may we leave from here? */
  149.     if (!(wp->leave & dirbits))
  150.         return 0;    /* nope */
  151. #ifdef SELECTOR_HACK
  152.     if (ip->mask & forbidden) {    /* may not be pushed by THIS object */
  153.         switch (obj[ty-dy][tx-dx]->pic) {
  154.         case 0:    /* man: */
  155.         case 7:    /* man in selector */
  156.         if (ip->mask == 0x30010) {    /* walkable selector */
  157.             sel_enter = 1;
  158.             new_sel_ip = ip;
  159.             goto move_ok;
  160.         }
  161.         }
  162.         return 0;
  163.     }
  164.     forbidden = (ip->mask & 0xffff) << 16;    /* for next object */
  165. #endif
  166.     tx += dx;
  167.     ty += dy;
  168.     wp = map[ty][tx];
  169.     if (!(wp->enter & dirbits))
  170.         return 0;                /* may not enter from here */
  171.     if (!(wp->mask & ip->mask))
  172.         return 0;                /* may not enter square anyway */
  173.     ip = obj[ty][tx];            /* next object, if any */
  174.     } while (ip);
  175.  
  176. move_ok:
  177.     ;
  178.     /* yeah, move is OK, do it! */
  179.     /* do the move, count blocks moved */
  180.     {   int n;
  181.     n = 0;
  182.     do {
  183.         ++n;
  184.         (obj[ty][tx] = obj[ty-dy][tx-dx])->chr = 1;    /* mark object 'moved' */
  185.         tx -= dx;
  186.         ty -= dy;
  187.     } while (tx != x || ty != y);
  188. #ifdef SELECTOR_HACK
  189.     if (sel_leave) {
  190.         obj[ty][tx] = sel_ip;
  191.         obj[ty+dy][tx+dx]->pic = 0;    /* normal man again */
  192.         sel_ip->chr = 1;
  193.         sel_ip = NULL;
  194.     } else {
  195.         obj[ty][tx] = NULL;
  196.     }
  197.     if (sel_enter) {
  198.         sel_ip = new_sel_ip;
  199.         obj[ty+n*dy][tx+n*dx]->pic = 7;    /* man in a box */
  200.     }
  201.     DOPAINT(x, y, x+n*dx, y+n*dy);
  202.     if (!sel_leave)
  203.         obj[ty][tx] = &blocked;
  204.  
  205. #else
  206.     obj[ty][tx] = NULL;
  207.     DOPAINT(x, y, x+n*dx, y+n*dy);
  208.     obj[ty][tx] = &blocked;
  209. #endif
  210.     return n;
  211.     }
  212. }
  213.  
  214. static int automoves(void) {
  215.     int x, y, flag = 0;
  216.  
  217.     for (x = game.numcols-3; x>1; --x)
  218.     for (y = game.numrows-3; y>1; --y) {
  219.         struct objects *ip;
  220.         if ((ip = obj[y][x])) {
  221.         if ((ip->pushdir & 1) && do_move(x, y, 0))
  222.             flag = 1;
  223.         else if ((ip->pushdir & 2) && do_move(x, y, 1))
  224.             flag = 1;
  225.         }
  226.     }
  227.     for (x = 2; x < game.numcols-2; ++x)
  228.     for (y = 2; y < game.numrows-2; ++y) {
  229.         struct objects *ip;
  230.         if ((ip = obj[y][x])) {
  231.         if ((ip->pushdir & 4) && do_move(x, y, 2))
  232.             flag = 1;
  233.         else if ((ip->pushdir & 8) && do_move(x, y, 3))
  234.             flag = 1;
  235.         }
  236.     }
  237.     return flag;    /* turn if at least one object was moved */
  238. }
  239.  
  240. #define TURN(object, bits)    ((((object) << (bits)) & 0x0f) | ((object) >> (4-(bits))))
  241.  
  242. static void check_effects(void) {
  243.     /* check moved objects, if they entered effect squares */
  244.     /* if so, apply effect                   */
  245.     /* mark objects unmoved and remove the blockers       */
  246.     int x, y;
  247.     for (x = 2; x < game.numcols-2; ++x)
  248.     for (y = 2; y < game.numrows-2; ++y)
  249.         if (obj[y][x] && obj[y][x]->chr) {
  250.         struct objects *ip;
  251.         ip = obj[y][x];
  252.         if (ip == &blocked) {
  253.             obj[y][x] = NULL;    /* square is free now */
  254.             if (map[y][x]->effect >= 2*E_ONCE)
  255.             /* square will change now to other type */
  256.             map[y][x] = walls + (map[y][x]->effect / E_ONCE) - 2;
  257.         } else {
  258.             int effect;
  259.             ip->chr = '\0';
  260.             if ((effect = map[y][x]->effect)) {
  261.             if (effect > E_ONCE && effect < 2 * E_ONCE)
  262.                 map[y][x] = walls;    /* one-time effect only */
  263.             effect %= E_ONCE;
  264.             switch (effect) {
  265.             case E_TURN_CCW:
  266.             case E_TURN_180:
  267.             case E_TURN_CW:
  268.                 /* does a rotation affect the object? */
  269.                 if ((ip->movedir != 0 && ip->movedir != 0x0f) ||
  270.                 (ip->pushdir != 0 && ip->pushdir != 0x0f)) {
  271.                 /* yes, it does. Are there 2 or 4 pictures for this tile? */
  272.                 if (TURN(ip->movedir, 2) == ip->movedir &&
  273.                     TURN(ip->pushdir, 2) == ip->pushdir)
  274.                     /* only two pictures */
  275.                     ip->pic ^= (effect & 1);
  276.                 else
  277.                     /* four pictures */
  278.                     ip->pic = (ip->pic & ~3) | ((ip->pic + effect) & 3);
  279.                 ip->movedir = TURN(ip->movedir, effect);
  280.                 ip->pushdir = TURN(ip->pushdir, effect);
  281.                     
  282.                 DOPAINT(x, y, x, y);
  283.                 }
  284.                 break;
  285.             case E_ADDPOWER:
  286.                 ++ip->power;
  287.                 break;
  288.             case E_SUBPOWER:
  289.                 if (ip->power)
  290.                 --ip->power;
  291.                 break;
  292.             case E_TELEPORT:
  293.                 {   int xx, yy;
  294.                 /* find a matching teleporter */
  295.                 for (xx = 2; xx < game.numcols-2; ++xx)
  296.                     for (yy = 2; yy < game.numrows-2; ++yy)
  297.                     if ((xx != x || yy != y) &&
  298.                         (!obj[yy][xx] || obj[yy][xx] == &blocked) &&
  299.                         map[yy][xx]->effect % E_ONCE == E_TELEPORT) {
  300.                         /* found a matching free teleporter */
  301.                         obj[yy][xx] = ip;
  302.                         if (map[yy][xx]->effect == E_TELEPORT+E_ONCE)
  303.                         map[yy][xx] = walls;
  304.                         obj[y][x] = NULL;
  305.                         if (gamegraphic) {
  306.                         doPaint(xx,yy,xx,yy);
  307.                         doPaint(x,y,x,y);
  308.                         }
  309.                         goto done;
  310.                     }
  311.                 }
  312.                 /* fall through if just a single teleporter, */
  313.                 /* or no other teleporter is free: ignore! */
  314.             done:
  315.                 break;
  316.             }
  317.             }
  318.         } /* moved object */
  319.         } /* any object at all */
  320. }
  321.  
  322. void playermove(int dir) {    /* dir is 0..3 */
  323.     int result;
  324.     struct objects *ip;
  325. #if 0
  326.     {
  327.     int x, y;
  328.     for (x = 2; x < game.numcols-2; ++x)
  329.             for (y = 2; y < game.numrows-2; ++y)
  330.         if (obj[y][x] && obj[y][x]->chr) {
  331.             printf("unclean object at (%d,%d) of chr %x\n", x, y, obj[y][x]->chr);
  332.             obj[y][x]->chr = '\0';
  333.         }
  334.     }
  335. #endif
  336.     (ip = obj[game.y][game.x])->pushdir = 1 << dir;
  337.  
  338. #ifdef SELECTOR_HACK
  339.     if (ip->pic == 7) /* no one may push from behind */
  340.     result = do_move(game.x, game.y, dir);
  341.     else
  342. #endif
  343.     {
  344.     int n = 0;
  345.     /* check if there are blocks behind us that help pushing */
  346.     /* stupid compiler, result IS initialized! */
  347.     do
  348.         ++n;
  349.     while (obj[game.y-n*dytab[dir]][game.x-n*dxtab[dir]]);
  350.     do
  351.         --n;
  352.     while (!(result = do_move(game.x-n*dxtab[dir], game.y-n*dytab[dir], dir)) && n);
  353.     if (result && result-1 < n)
  354.         fatal("internal error\n");
  355.     }
  356.     ip->pushdir = 0;    /* this must be reset BEFORE check_effects! */
  357.  
  358.     if (result) {
  359.     int x, y;
  360.     storemove(result, dir);
  361.     do {
  362.         if (gamegraphic)
  363.         sync_and_wait();
  364.         /* add sound here too! */
  365.         check_effects();
  366.     } while (automoves());
  367.     /* player may get pushed around: search him! */
  368.     game.x = game.y = 0;
  369.     for (x = 2; x < game.numcols-2; ++x)
  370.             for (y = 2; y < game.numrows-2; ++y)
  371.         if (obj[y][x] == ip) {
  372.             game.y = y;
  373.             game.x = x;
  374.             break;
  375.         }
  376.     /* fatal("Ooops, player has vanished!\n"); */
  377.     if (finished()) {
  378.         int update = 0;
  379.         cmd_ReadHighscores();    /* read the actual values! TODO: lock the highscore file */
  380.         if (highscore[game.level] < game.score) {
  381.         highscore[game.level] = game.score;
  382.         update = 1;
  383.         }
  384.         if (highscore[game.level+100] > game.n_moves) {
  385.         highscore[game.level+100] = game.n_moves;
  386.         update |= 2;
  387.         }
  388.         if (highscore[game.level+200] > game.n_pushes) {
  389.         highscore[game.level+200] = game.n_pushes;
  390.         update |= 4;
  391.         }
  392.         if (update) {
  393.         const char *firstfile = NULL;
  394.         WriteHighscores();
  395. #if 0
  396.         if (highscore[game.level] == game.score)
  397.             save_game("sol");    /* optimum score */
  398.         else if (highscore[game.level+200] == game.n_pushes)
  399.             save_game("mp");    /* minimal pushes */
  400.         else if (highscore[game.level+100] == game.n_moves)
  401.             save_game("mm");    /* minimal moves */
  402.         /* TODO: should remove obsolete files and rename others. That's hard! */
  403. #else
  404.         /* possibly save multiple files */
  405.         if (highscore[game.level] == game.score)
  406.             save_game(firstfile = "bs");    /* best score */
  407.         if (highscore[game.level+200] == game.n_pushes)
  408.             firstfile ? link_game(firstfile, "mp") :
  409.             save_game(firstfile = "mp");    /* minimal pushes */
  410.         if (highscore[game.level+100] == game.n_moves)
  411.             firstfile ? link_game(firstfile, "mm") :
  412.             save_game(firstfile = "mm");    /* minimal moves */
  413. #endif
  414.         show_message(TXT_NEWHIGH, game.score);
  415.         return;
  416.         }
  417.         /* TODO: release the highscore file lock */
  418.         show_message(TXT_YOU_WIN, game.score, highscore[game.level]);
  419.         return;
  420.     } /* if (finished()) */
  421.     cmd_ShowScore();
  422.     } else
  423.     if (!game.finished)    /* hack to avoid complains on excess moves at the end */
  424.         show_message(TXT_MOVENOTPOSSIBLE);
  425. }
  426.